/****************************************************************
 * I am the AC 130 vs Pirate game. I allow the user to control an 
 * AC130 gunship in the hunt for Modern day pirates. The player  
 * will be allowed to roam free across a section of ocean/coastline.  
 * In the water there will be four types of vessels: Merchant  
 * ships that the player must protect, Pirate ships that the player  
 * must identify and destroy, Fishing boats that look like Pirate  
 * ships that the player must not destroy, and Friendly Navy ships  
 * that can capture pirates for extra points. Time permitting there  
 * may also be ground targets to attack. 
 *
 * Key elements of this project will be the Player/AC130, the Ships,  
 * the ShipAI system, the collision grid, the debris controller, the  
 * Ground/Water, Clouds, and scoring system. This project will make  
 * use of an external text file to store useful game parameters,  
 * thus allowing us to play balance the game without recompiling  
 * the executable. 
 *****************************************************************
 * I am the Game Data class. I contain all the data that needs to 
 * be shared between screens. I also contain a hash table for data 
 * read in from a file.
 *****************************************************************/

#include "GameData.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

GameData::GameData(){
		// set all elements to NULL
	for(int itor = 0; itor < HASHTBLMAX; itor++){
		hashTable[itor] = NULL;
	}
		// reade GameData file C:\\Code\\AC130vPirates\\GameData.txt
	FILE * fin = fopen("GameData.txt", "r");
	ReadFile(fin);
	if(fin != NULL)	fclose(fin);
		// return string set to empty string
	hashRet[0]= '\0';
	debugTxt[0]= '\0';
		// start game in on first screen
	nxtScreen = stGamePlay;	// the screen that we will go to on the screen swap

		// set default input
	mouseX = 0;
	mouseY = 0;
	lmouse = false;
	rmouse = false;
	for (int itor = 0; itor < KEY_MAX; itor ++){
		oldKey[itor] = false;
	}
}
	
/// Remove all initilized Hash Table elements
GameData::~GameData(){
	for(int itor = 0; itor < HASHTBLMAX; itor++){
			// HashNode does a recursive delete
		if(hashTable[itor] != NULL)
			delete(hashTable[itor]);
	}
}

/// I am the file reader. I parse each character of the file 
/// according to one of three modes. Comment Mode with a line starting
/// with '/', Text mode with a key and a text value divided by ':',
/// and Number mode with a key and a float divided by '='. 
/// I then process the non comment lines into the hash table.
void GameData::ReadFile(FILE *fin){
	char cval = ' ';
	int isOne = 1;
	int keyind;
	int valind;
	char keyAr[HASHKEYLEN];
	char valAr[HASHSTRLEN];
	bool isNum = false;
		// ensure it is a valid file
	if(fin == NULL) return;
		// itorate on each line assume file will not be longer than 2048
	for (int litor = 0; litor < 2048; litor++){
		isOne = fscanf(fin, "%c", &cval);
		if(isOne != 1) return;	// exit on EOF
		// check for comment or blank line
		if(cval == '/' || cval == '\n' || cval == ' '){
			for(int itor =0; itor < HASHSTRLEN && cval!='\n'; itor++){
				// ignore comment text
				isOne = fscanf(fin, "%c", &cval);
				if(isOne != 1) return;	// exit on EOF
			}
		}else{
				// set first char of the line to first key element
			keyind=0;
			keyAr[keyind] = cval;keyAr[keyind+1] = '\0';
			
				// loop untill '\n' or  ':' or '=' is found
			for(int itor =0; itor < HASHKEYLEN && 
				cval!='\n' && cval != ':' && cval != '='; itor++)
			{
				keyind++;	// incriment to new index
				isOne = fscanf(fin, "%c", &cval);	// read next character
				if(isOne != 1) return;	// exit on EOF
				keyAr[keyind] = cval;keyAr[keyind+1] = '\0';
			}
				// check last character read in for string read
			if( keyAr[keyind] == ':'){
				valind = -1;
					// read untill new line
				for(int itor =0; itor < HASHSTRLEN && cval!='\n'; itor++){
					valind++;	// incriment to new index
					isOne = fscanf(fin, "%c", &cval);	// read next character
					if(isOne != 1) return;	// exit on EOF
					valAr[valind] = cval; valAr[valind+1] = '\0';
				}	// overwrite '\n' newline character
				valAr[valind] = '\0';
				keyAr[keyind] = '\0';
					//send data to hash table
				AddStrToHash(keyAr, valAr);
			}
			// check last character read in for Number read
			if( keyAr[keyind] == '='){
				valind = -1;
					// read untill new line
				for(int itor =0; itor < HASHSTRLEN && cval!='\n'; itor++){
					valind++;	// incriment to new index
					isOne = fscanf(fin, "%c", &cval);	// read next character
					if(isOne != 1) return;	// exit on EOF
					valAr[valind] = cval; valAr[valind+1] = '\0';
				}	// overwrite '\n' newline character
				valAr[valind] = '\0';
				keyAr[keyind] = '\0';
					//send data to hash table
				AddNumToHash(keyAr, valAr);
			}
		}	// end non comment line reader

	}	// end for loop through array

}

/// I am the Hashing function. I multiply each character by a prime number
/// to hopefully get a decent hash
int GameData::GetHash(char * key){
	int len = strnlen(key, HASHKEYLEN);
	int primes[7] = {17,13,11,7,5,3,2};
	int ret = 0;
		// add up the key characters muliplying by
		// prime numbers
	for(int itor =0; itor < len; itor++){
		ret += key[itor]*primes[itor%7];
	}
	// return within the hash table limits
	return ret%HASHTBLMAX;
}

/// I am the numeric Node addion function. If the array in val
/// is not numeric I don't add a node to the array.
/// conversion is done with atof, which will result in Nan or
/// +-Inf
void GameData::AddNumToHash(char * key, char * val){
	HashNode * addMe = new HashNode;
	double num = atof(val);
	int index = GetHash(key);
		// only add if num is a valid number
	if(num > -9999999 && num < 9999999){
		
		// initilize the node to its numeric value.
		// also copy the current hashTable value for
		// collision resolution.
		addMe->InitNum(key, (float)(num), hashTable[index]);
			// add addMe to the hash table
		hashTable[index] = addMe;
	}
}

void GameData::AddStrToHash(char * key, char * val){
	HashNode * addMe = new HashNode;
	int index = GetHash(key);
		// initilize the node to its numeric value.
		// also copy the current hashTable value for
		// collision resolution.
	addMe->InitStr(key, val, hashTable[index]);
		// add addMe to the hash table
	hashTable[index] = addMe;
}

/// I am the number retrival function. I return 0.0 if
/// the key is not found and the key's numeric value 
/// if it is found.
float GameData::GetNum(char * key){
	int index = GetHash(key);
	HashNode * trip = hashTable[index];
		// search allong collision linked list
	for(int itor =0; itor < 1024 && trip != NULL; itor++){
			// detect matching node
		if(trip->isNum == true && strncmp(key,trip->key, HASHKEYLEN) == 0)
		{
			return trip->num;
		}
			//step to next linked list ellement
		trip = trip->next;
	}
	return 0.0;
}

/// I am the string retrival function. I return the empty 
/// string "\0" if key is not found, and the string associated
/// to the key if it is found. Note the string is coppied into
/// hashRet. If you want the latest string value accessed by
/// this funciton, look there.
char * GameData::GetStr(char * key){
	int index = GetHash(key);
	HashNode * trip = hashTable[index];
		// reset hash ret
	hashRet[0] = '\0';
		// search allong collision linked list
	for(int itor =0; itor < 1024 && trip != NULL; itor++){
			// detect matching node
		if(trip->isNum == false && strncmp(key,trip->key, HASHKEYLEN) == 0)
		{	// copy hashRet
			strncpy(hashRet, trip->str, HASHSTRLEN);
			return hashRet;
		}
			//step to next linked list ellement
		trip = trip->next;
	}
	return hashRet;
}
